package QC.MessageBox.service;

import QC.MessageBox.beans.*;
import QC.MessageBox.common.PeriodicTime;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;

/**
 * Created by Andaren on 2017/4/11.
 * Used for: 综合数据访问
 */
public class DataAccess {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataAccess.class);

    private MySQLService sqlService;

    private RedisService redisService;

    public DataAccess(MySQLService sqlService, RedisService redisService) {
        this.sqlService = sqlService;
        this.redisService = redisService;
    }

    /**
     * 查找公众号信息
     *      优先查缓存
     * @param developerId
     * @return
     */
    public Future<WXPublic> getWXPublicInfoByDeveloperId(String developerId) {
        Future<WXPublic> fut = Future.future();
        redisService.getPublicInfoByDeveloperId(developerId).setHandler(redisAsyn -> {
           if (redisAsyn.succeeded() && redisAsyn.result() != null) {
               fut.complete(redisAsyn.result());
           } else {
               LOGGER.info("redis hit failed in searching publicInfo.");
               sqlService.getPublicInfoByDeveloperId(developerId).setHandler(sqlRes -> {
                   if (sqlRes.succeeded()) {
                       fut.complete(sqlRes.result());
                   } else {
                       LOGGER.info("查找该公众号信息失败：developerId ->[{}]", developerId);
                       fut.fail(sqlRes.cause());
                   }
               });
           }
        });
        return fut;
    }

    /**
     * 获取app用户TalkId
     * @param openId
     * @return
     */
    public Future<AppUser> getAppUser(String openId) {
        Future<AppUser> talkFut = Future.future();
        redisService.get(RedisService.REDIS_KEY_APP_USER_TALKID + ":" + openId).setHandler(as-> {
            if (as.succeeded() && as.result() != null) {
                AppUser appUser = Json.decodeValue(as.result(), AppUser.class);
                talkFut.complete(appUser);
            } else {
                LOGGER.info("redis hit failed in searching appUser.");
                sqlService.getCSInfo(openId).setHandler(sqlAs -> {
                    if (sqlAs.succeeded()) {
                        AppUser appUser = sqlAs.result();
                        if(StringUtils.isNotBlank(appUser.getTalkId())) {
                            redisService.set(RedisService.REDIS_KEY_APP_USER_TALKID + ":" + openId, Json.encodePrettily(appUser)).setHandler(redisAs -> {
                                if (redisAs.succeeded()) {
                                    talkFut.complete(appUser);
                                } else {
                                    talkFut.fail(redisAs.cause());
                                }
                            });
                        } else {
                            talkFut.complete(null);
                        }
                    } else {
                        talkFut.fail(sqlAs.cause());
                    }
                });
            }
        });
        return talkFut;
    }

    /**
     * 获取随机app客服信息
     * @return
     */
    public Future<CustomerServ> getCSInfoByRandom() {
        Future<CustomerServ> randCSFu = Future.future();
        redisService.getRandomCS().setHandler(as -> {
           if (as.succeeded() && as.result() != null) {
               randCSFu.complete(as.result());
           } else {
               // 创建一个随机客服
               Random r = new Random(System.nanoTime() / 47);
               int randCsId = r.nextInt(10000);
               CustomerServ customerServ = new CustomerServ();
               customerServ.setTalkId(String.valueOf(randCsId));
               customerServ.setId(RedisService.REDIS_RANDOM_CS_ID.addAndGet(1));
               redisService.saveCustomerService(customerServ)
                       .compose((Void) ->
                           randCSFu.complete(customerServ)
                       , randCSFu);
           }
        });
        return randCSFu;
    }

    /**
     * 新增微信用户
     * @param user
     * @return
     */
    public Future<WXUser> insertWxUser(WXUser user) {
        Future insertFut = Future.future();
        sqlService.insertWxUser(user).setHandler(insertAs -> {
           if (insertAs.failed()) {
               insertFut.fail(insertAs.cause());
           } else {
               String userJsonStr = Json.encode(insertAs.result());
               redisService.set(RedisService.REDIS_KEY_WX_USER + ":" + user.getWxOpenId(), userJsonStr)
                       .setHandler(redisAs -> {
                          if (redisAs.failed()) {
                              LOGGER.info("缓存微信用户异常：[openId=" + user.getWxOpenId() + "]");
                          }
                       });
               insertFut.complete(insertAs.result());
           }
        });
        return insertFut;
    }

    /**
     * 根据openid查找对应微信用户
     * @param openId
     * @return
     */
    public Future<WXUser> findWxUserByOpenId(String openId) {
        Future userFut = Future.future();
        redisService.get(RedisService.REDIS_KEY_WX_USER + ":" + openId).setHandler(redisAs -> {
            if (redisAs.failed() || redisAs.result() == null) {
                sqlService.getWxUserByOpenId(openId).setHandler(sqlAs-> {
                   if (sqlAs.failed()) {
                       userFut.fail(sqlAs.cause());
                   } else {
                       WXUser user = sqlAs.result();
                       redisService.set(RedisService.REDIS_KEY_WX_USER + ":" + openId, Json.encodePrettily(user))
                               .setHandler(redisSetAs -> {
                                   if (redisSetAs.failed()) {
                                       LOGGER.info("缓存微信用户异常：[openId=" + user.getWxOpenId() + "]");
                                   }
                               });
                       userFut.complete(user);
                   }
                });
            } else {
                WXUser user = Json.decodeValue(redisAs.result(), WXUser.class);
                userFut.complete(user);
            }
        });
        return userFut;
    }

    /**
     * 保存app用户-微信用户映射
     * @param user
     * @return
     */
    public Future<AppUser> saveAppUser(AppUser user) {
        Future<AppUser> saveFut = Future.future();
        sqlService.save(user).setHandler(saveAs -> {
           if (saveAs.failed()) {
               saveFut.fail(saveAs.cause());
           } else {
               AppUser newUser = saveAs.result();
               if (newUser != null) {
                   redisService.set(RedisService.REDIS_KEY_APP_USER_TALKID + ":" + newUser.getOpenId(), Json.encodePrettily(newUser));
                   saveFut.complete(newUser);
               } else {
                   saveFut.complete(null);
               }
           }
        });
        return saveFut;
    }

    /**
     * 用户取消关注时，删除会话映射关系和用户信息
     * @param openId
     * @return
     */
    public Future<Void> deleteAppUserByOpenId(String openId) {
        Future<Void> deleteFut = Future.future();

        Future<Void> delAPPUserInDB = sqlService.deleteAppUserByOpenId(openId);
        Future<Void> delWXUserInDB = sqlService.deleteWXUserByOpenId(openId);
        Future<Void> delWXUserInRedis = redisService.delete(RedisService.REDIS_KEY_WX_USER + ":" + openId);
        Future<Void> delAppUserInRedis = redisService.delete(RedisService.REDIS_KEY_APP_USER_TALKID + ":" + openId);
        Future<Void> updateCusServDataCurrentUse = changeCusServDataCurrent(openId);
        List<Future> compositeAllFutList = new ArrayList<>();
        compositeAllFutList.add(delAPPUserInDB);
        compositeAllFutList.add(delWXUserInDB);
        compositeAllFutList.add(delWXUserInRedis);
        compositeAllFutList.add(delAppUserInRedis);
        compositeAllFutList.add(updateCusServDataCurrentUse);
        CompositeFuture.all(compositeAllFutList).setHandler(compositeAs -> {
            if (compositeAs.failed()) {
                deleteFut.fail(compositeAs.cause());
            } else {
                deleteFut.complete();
            }
        });
        return deleteFut;
    }

    /**
     * 更新公众号信息
     * @param pub
     * @return
     */
    public Future<Void> updatePublic(WXPublic pub) {
        Future<Void> updateFut = Future.future();
        sqlService.update(pub).setHandler(updateAs -> {
            if (updateAs.failed()) {
                updateFut.fail(updateAs.cause());
            } else {
                redisService.savePublicToRedis(pub);
                updateFut.complete();
            }
        });
        return updateFut;
    }

    /**
     * 保存公众号信息
     * @param pub
     * @return
     */
    public Future<WXPublic> savePublic(WXPublic pub) {
        Future<WXPublic> saveFut = Future.future();
        // 检验该公众号是否已存在
        getWXPublicInfoByDeveloperId(pub.getDeveloperId())
                .setHandler(oldPubAr -> {
                   if (oldPubAr.succeeded()) {
                       LOGGER.info("公众号[developerId={}]已存在", pub.getDeveloperId());
                       saveFut.fail("公众号已存在");
                   } else {
                       sqlService.save(pub).setHandler(saveAs -> {
                           if (saveAs.failed()) {
                               saveFut.fail(saveAs.cause());
                           } else {
                               saveFut.complete(saveAs.result());
                           }
                       });
                   }
                });
        return saveFut;
    }

    /**
     * 客服转接，更新微信用户对话指向
     * @param csId
     * @param newCs
     * @param developerId
     * @return
     */
    public Future<Void> transferCsTalkId(String csId, AppUser newCs, String developerId) {
        Future<Void> transferFut = Future.future();
        sqlService.getAppUserOpenId(csId).setHandler(openIdAs -> {
           if (openIdAs.failed()) {
               transferFut.fail(openIdAs.cause());
           } else {
               List<String> openIdList = openIdAs.result();
               if (openIdList != null && openIdList.size() > 0) {
                   List<Future> transferFurList = new ArrayList<>();
                   for (String openId : openIdList) {
                       Future saveChangeFut = Future.future();
                       getAppUser(openId)
                               .compose(appUser -> {
                                   appUser.setTalkId(newCs.getTalkId())
                                           .setCsId(newCs.getCsId())
                                           .setNickname(newCs.getNickname());
                                   // 更改redis中的openid-talkId映射为openid-newTalkid
                                   redisService.set(RedisService.REDIS_KEY_APP_USER_TALKID + ":" + openId, Json.encodePrettily(appUser));
                               }, saveChangeFut);
                       transferFurList.add(saveChangeFut);
                   }
                   CompositeFuture.all(transferFurList)
                           .compose(compositeFutureAr -> {
                               // 更改数据库中的映射
                               sqlService.updateAppUserInfoByCsId(csId, newCs)
                                       .setHandler(transferFut.completer());
                           },transferFut);
               }
           }
        });
        return transferFut;
    }

    /**
     * 客服转接，更新微信用户对话指向
     * @param openIds
     * @param newCs
     * @param developerId
     * @return
     */
    public Future<Void> transferCsTalkIdByList(JsonArray openIds, AppUser newCs, String developerId) {
        Future<Void> transferFut = Future.future();
        List<String> openIdList = openIds.getList();
        if (openIdList != null && openIdList.size() > 0) {
            List<Future> transferFurList = new ArrayList<>();
            for (String openId : openIdList) {
                Future singleCsTransferFut = Future.future();
                saveChangesInTransferCs(openId, newCs, developerId)
                        .setHandler(singleCsTransferFut.completer());
                transferFurList.add(singleCsTransferFut);
            }
            CompositeFuture.all(transferFurList)
                    .compose(compositeFutureAr -> {
                        // 更改数据库中的映射
                        sqlService.updateAppUserTalkIdByList(openIds, newCs).setHandler(Future::succeededFuture);
                    },transferFut);
        }
        return transferFut;
    }

    public Future saveChangesInTransferCs (String openId, AppUser newCs, String developerId) {
        Future saveChangeFut = Future.future();
        getAppUser(openId)
                .compose(appUser -> {
                    appUser.setTalkId(newCs.getTalkId())
                            .setCsId(newCs.getCsId())
                            .setNickname(newCs.getNickname());
                    // 更改redis中的openid-talkId映射为openid-newTalkid
                    redisService.set(RedisService.REDIS_KEY_APP_USER_TALKID + ":" + openId, Json.encodePrettily(appUser));
                    // 更改该粉丝对应客服服务中的客服服务状态
                    changeCusServDataCurrent(openId)
                            .compose((Void) -> {
                                // 生成客服服务数据记录
                                CusServiceData cusServiceData = new CusServiceData();
                                cusServiceData.setActiveCount(0L)
                                        .setCsId(newCs.getCsId())
                                        .setCreateTime(PeriodicTime.getCurrentTimestamp())
                                        .setCurrentUse("0")
                                        /**
                                         * TODO 全渠道下，这里应该传对应渠道下的receiverId
                                         */
                                        .setDeveloperId(developerId)
                                        .setOpenId(openId);
                                sqlService.save(cusServiceData).setHandler(saveChangeFut.completer());
                            }, saveChangeFut);
                }, saveChangeFut);
        return saveChangeFut;
    }

    /**
     * 改变客服服务记录使用状态
     * @param openId
     * @return
     */
    public Future<Void> changeCusServDataCurrent(String openId) {
        Future<Void> changeFut = Future.future();
        sqlService.updateCusServDataStateOrCountByOpenId(openId, false)
                .compose((Void) -> {
                    changeFut.complete();
                }, changeFut);
        return changeFut;
    }

    /**
     * 更新客服互动次数
     * @param openId
     * @return
     */
    public Future<Void> countCusServData(String openId) {
        Future<Void> countFut = Future.future();
        sqlService.updateCusServDataStateOrCountByOpenId(openId, true)
                .compose((Void) -> {
                    countFut.complete();
                }, countFut);
        return countFut;
    }

    /**
     *  检查缓存中是否存在该电话号码对应的Web粉丝信息
     * @param userPhone  现在为电话号码
     * @return
     */
//    public Future<Optional<WebUserMap>> checkWebUser(String userPhone) {
//        return Future.future(check -> {
//            redisService.get(RedisService.REDIS_KEY_WEB_USER_CHAT_ID + ":" + userPhone)
//                    .compose(webUserInfo -> {
//                        if (StringUtils.isEmpty(webUserInfo) || webUserInfo.equals("null")) {
//                            check.complete(Optional.empty());
//                        } else {
//                            WebUserMap webUserMap = Json.decodeValue(webUserInfo, WebUserMap.class);
//                            check.complete(Optional.of(webUserMap));
//                        }
//                    }, check);
//        });
//    }

    /**
     *  检查缓存中是否存在该电话号码对应的Web粉丝信息
     * @param userPhone  现在为电话号码
     * @return
     */
    public Future<Optional<AppUser>> checkWebUser(String userPhone) {
        return Future.future(checkFut -> {
            getAppUser(userPhone).setHandler(checkAr -> {
               if (checkAr.succeeded()) {
                    if (checkAr.result() != null) {
                        checkFut.complete(Optional.of(checkAr.result()));
                    } else {
                        checkFut.complete(Optional.empty());
                    }
               } else {
                   checkFut.fail(checkAr.cause());
               }
            });
        });
    }

    /**
     * 验证渠道用户是否被拉黑
     * @param channelUserId
     * @return true:被拉黑,false:没有被拉黑
     */
    public Future<Boolean> checkChannelUserPulledBlack(String channelUserId) {
        return Future.future(checkFut -> {
            getAppUser(channelUserId)
                    .compose(appUser -> {
                       if (appUser != null && appUser.getPullBlack() != null && appUser.getPullBlack() == 1) {
                           checkFut.complete(true);
                       } else {
                           checkFut.complete(false);
                       }
                    }, checkFut);
        });
    }

    /**
     * 微博(执行/取消)拉黑操作
     * @param uidList
     * @param pullBlack
     * @return
     */
    public Future<Void> setPullBlackStatusForWB(List<String> uidList, boolean pullBlack) {
        return Future.future(wbPullFut -> {
            Future<Void> wbCacheFut = redisService.setWBPullBlackCache(uidList, pullBlack);
            Future<Void> wbPullBlackUpdateFut = sqlService.setWBPullBlackStatus(uidList, pullBlack);
            CompositeFuture.all(wbCacheFut, wbPullBlackUpdateFut)
                    .compose(completed -> {
                        wbPullFut.complete();
                    }, wbPullFut);
        });
    }

    /**
     * 更新web粉丝在线状态
     * @param webUserMap
     * @return
     */
    public Future<Void> updateWebFansOnline(WebUserMap webUserMap) {
        return Future.future(webUserOnlineUpdateFut -> {
            Future<Void> updateRedisFut = redisService.set(
                    RedisService.REDIS_KEY_WEB_USER_CHAT_ID+":"+webUserMap.getFansPhone(),
                    Json.encode(webUserMap));
            Future<Void> updateDBFut = sqlService.updateWebFansOnlineStatus(webUserMap.getFansPhone(), webUserMap.getOnlineStatus());
            CompositeFuture.all(updateRedisFut, updateDBFut)
                    .compose(completed -> {
                        webUserOnlineUpdateFut.complete();
                    }, webUserOnlineUpdateFut);
        });
    }

}
